package com.hmc.modules.sjfx.util;

import com.google.common.collect.Lists;

import java.util.ArrayList;
import java.util.List;

/**
 * Java正态分布工具类
 */
public class ZtnhUtil {
    /**
     * 计算样本中特征值的平均值
     *
     * @param list 样本中的 featureValue list，不能为空
     * @return 平均值
     * @throws IllegalArgumentException 如果列表为空
     */
    public Double getMean(List<Double> list) {
        if (list.isEmpty()) {
            throw new IllegalArgumentException("List cannot be empty");
        }
        return list.stream().mapToDouble(Double::valueOf).average().orElse(0);
    }

    /**
     * 计算样本的方差（标准差的平方）
     *
     * @param list 样本中的featureValue list，不能为空
     * @param mean 样本的平均值
     * @return 方差
     * @throws IllegalArgumentException 如果列表为空
     */
    private Double getSigma(List<Double> list, Double mean) {
        if (list.isEmpty()) {
            throw new IllegalArgumentException("List cannot be empty");
        }
        /**
         * 使用stream()将列表转换为流，mapToDouble()将每个元素转换为双精度浮点数形式。
         * 在mapToDouble中，计算每个元素与平均值的差的平方。
         * sum()方法用于求和，计算所有差的平方之和。
         * 将和除以列表大小，得到平均值。
         * 最后，使用Math.pow()对平均值进行开方运算，返回标准差。
         */
        return Math.pow(list.stream().mapToDouble(item -> Math.pow(item - mean, 2)).sum() / list.size(), 0.5);
    }

    /**
     * 生成正态分布拟合曲线的数据点列表
     *
     * @param usl 控制上限
     * @param lsl 控制下限
     * @param mean 均值
     * @param sigma 方差
     * @param max 曲线的x轴最大值
     * @param min 曲线的x轴最小值
     * @return 包含正态分布曲线数据点的列表
     * @throws IllegalArgumentException 如果控制限或均值的顺序不正确，或调整后的正常范围宽度为零或负数
     */
    public List<List<Double>> featureNormal(Double usl, Double lsl, Double mean, Double sigma, Double max, Double min) {
        if (lsl > mean || mean > usl || min > lsl || max < usl) {
            throw new IllegalArgumentException("Invalid input: control limits or mean are not in the correct order");
        }

        List<List<Double>> featureNormal = new ArrayList<>();
        double normalRange = (max - min) / 10;
        double normalBegin = Math.min(mean - normalRange / 2, lsl);
        double normalEnd = Math.max(mean + normalRange / 2, usl);

        // 调整正常范围并确保其保持在[min, max]内
        double rangeBegin = Math.max(normalBegin - Math.abs(normalBegin * 0.2), min);
        double rangeEnd = Math.min(normalEnd + Math.abs(normalEnd * 0.2), max);
        double normalRangeWidth = rangeEnd - rangeBegin;

        // 确保宽度不为负，防止除以零
        if (normalRangeWidth <= 0) {
            throw new IllegalArgumentException("The adjusted normal range has zero or negative width");
        }

        // 均分300个区间来绘制正态分布曲线
        Integer normalSize = 300;
        double normalWidth = normalRangeWidth / normalSize;
        for (int i = 0; i < normalSize; i++) {
            double x = rangeBegin + i * normalWidth;
            featureNormal.add(List.of(x, normalDistribution(x, mean, sigma)));
        }
        return featureNormal;
    }

    /**
     * 计算给定点在正态分布下的概率密度值
     *
     * @param x x轴的值
     * @param mean 均值
     * @param sigma 方差
     * @return 正态分布函数的值
     */
    private Double normalDistribution(double x, double mean, double sigma) {
        return (1 / (sigma * Math.sqrt(2 * Math.PI))) * Math.exp(-Math.pow((x - mean), 2) / (2 * Math.pow(sigma, 2)));
    }
}
